/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package phidgetmotion.gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;

import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import phidgetmotion.Error;
import phidgetmotion.classifier.Model;
import phidgetmotion.classifier.ModelTrainerListener;
import phidgetmotion.classifier.Model.ModelTrainer;
import phidgetmotion.gui.util.BindingTextBox;
import phidgetmotion.gui.util.ClassLabelModel;
import phidgetmotion.gui.util.IntegerDataSource;
import phidgetmotion.gui.util.NamedBoxLayoutPanel;

public class TrainPanel extends JPanel {
	private static final long serialVersionUID = 1L;
	private static final String notSaved = "not saved";
	
	private int maxTrainigSamples = 20;

	private final JTextField fileName = new JTextField(notSaved);
	private final JButton createNewModelButton = new JButton("Create");
	private final JButton loadModelButton = new JButton("Open");
	private final JButton saveModelButton = new JButton("Save");
	private final JButton saveModelAsButton = new JButton("Save as");
	
	private final JButton createNewClassButton = new JButton("Add Class");	
	private final JButton removeClassButton = new JButton("Remove Class");
	private final JButton removeDataButton = new JButton("Remove Data");
	private final JButton removeAllDataButton = new JButton("Remove All Data");
	private final JButton trainDataButton = new JButton("Train");
	
	private final BindingTextBox maxTrainigSamplesTextBox = new BindingTextBox("Maximal Training Samples", new IntegerDataSource(false) {
		
		@Override
		protected void setData(int data) {
			maxTrainigSamples = data;
		}
		
		@Override
		protected int getData() {
			return maxTrainigSamples;
		}
	});

	private final BindingTextBox updateInterval = new BindingTextBox("Update Interval", new IntegerDataSource(false) {
		
		@Override
		protected void setData(int data) {
			Model model = Model.getModel();
			if(model!=null) model.setUpdateInterval(data);
		}
		
		@Override
		protected int getData() {
			Model model = Model.getModel();
			if(model!=null) return model.getUpdateInterval();
			else return 0;
		}
	});
	private final BindingTextBox kNearest = new BindingTextBox("kNearest", new IntegerDataSource(false) {
		
		@Override
		protected void setData(int data) {
			Model model = Model.getModel();
			if(model!=null) model.setkNearest(data);
		}
		
		@Override
		protected int getData() {
			Model model = Model.getModel();
			if(model!=null) return model.getkNearest();
			else return 0;
		}
	});
	private final ClassLabelModel classLabelModel = new ClassLabelModel();
	private final JList classLabels = new JList();
	
	private File modelFile;
	private ModelTrainer modelTrainer = null;
	
	ActionListener createNewModel = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			modelFile = null;
			Model.create(1000);
			refreshGUI();
		}
	};
	
	ActionListener openModel = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			fileChooser.setMultiSelectionEnabled(false);
			fileChooser.setFileFilter(new FileNameExtensionFilter("Model files", "pmm"));

			if (fileChooser != null && fileChooser.showOpenDialog(TrainPanel.this) == JFileChooser.APPROVE_OPTION) {
				modelFile = fileChooser.getSelectedFile();
				try {
					Model.load(modelFile);
				}  catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
		}
	};
	
	ActionListener saveModelAs = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			if(model!=null){
				JFileChooser fileChooser = new JFileChooser();
				fileChooser.setCurrentDirectory(new File("."));
				fileChooser.setMultiSelectionEnabled(false);
				fileChooser.setFileFilter(new FileNameExtensionFilter("Model files", "pmm"));

				if (fileChooser != null && fileChooser.showSaveDialog(TrainPanel.this) == JFileChooser.APPROVE_OPTION) {
					modelFile = fileChooser.getSelectedFile();
					if(!modelFile.getPath().toLowerCase().endsWith(".pmm")) modelFile = new File(modelFile.getPath()+".pmm");
					try {
						model.save(modelFile);
					} catch (IOException e) {
						Error.error(e);
					}
					refreshGUI();
				}	
			}
	}};
	
	ActionListener saveModel = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			if(model!=null && modelFile!=null){
				try {
					model.save(modelFile);
				} catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
	}};
	
	ActionListener addClassLabel = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			if(model!=null){
				String name = JOptionPane.showInputDialog("Name");
				if(name!=null){
					model.addClass(name);
					refreshGUI();
				}
			}
		}
	};
	
	ActionListener removeClassLabel = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			int selectedIndex = classLabels.getSelectedIndex();
			if(model!=null && selectedIndex>-1){
				model.removeClass(selectedIndex);
				refreshGUI();
			}
			
		}
	};
	
	ActionListener removeData = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			int selectedIndex = classLabels.getSelectedIndex();
			if(model!=null && selectedIndex>-1){
				model.removeData(selectedIndex);
				refreshGUI();
			}
			
		}
	};
	
	ActionListener removeAllData = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			Model model = Model.getModel();
			if(model!=null){
				model.removeAllData();
				refreshGUI();
			}
		}
	};
	
	ModelTrainerListener modelTrainerListener = new ModelTrainerListener() {
		@Override
		public void updated(int classId) {
			classLabelModel.update(classId);
		}
		
		@Override
		public void finished(int classId) {
			modelTrainer = null;
			refreshGUI();
			trainDataButton.setText("Train");
		}
	};
	
	ActionListener train = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent ae) {
			if(modelTrainer!=null) modelTrainer.stop();
			else{
				Model model = Model.getModel();
				int selectedIndex = classLabels.getSelectedIndex();
				if(model!=null && selectedIndex>-1){
					modelTrainer = model.getModelTrainer(selectedIndex, maxTrainigSamples);
					modelTrainer.addModelTrainerListener(modelTrainerListener);
					refreshGUI();
					trainDataButton.setText("Stop Training");
				}
			}
		}
	};
	private ListSelectionListener classListChangeListener = new ListSelectionListener() {
		@Override
		public void valueChanged(ListSelectionEvent e) {
			refreshButtonState();
		}
	};
	
	public TrainPanel(){
		this(null);
	}
	
	public TrainPanel(File modelFile) {
		this.modelFile = modelFile;
		initComponents();
		refreshGUI();
	}
	
	private void refreshGUI() {
		if(modelFile!=null)fileName.setText(modelFile.getName());
		else fileName.setText(notSaved);
		createNewModelButton.setEnabled(modelTrainer==null);
		loadModelButton.setEnabled(modelTrainer==null);
		saveModelButton.setEnabled(modelTrainer==null && modelFile!=null);
		saveModelAsButton.setEnabled(modelTrainer==null);
		Model model = Model.getModel();
		if(model!=null && modelTrainer==null){
			updateInterval.setEditable(!model.containsData());
			updateInterval.updateData();
			kNearest.setEditable(true);
			kNearest.updateData();
			classLabelModel.reload();
			createNewClassButton.setEnabled(true);
			classLabels.setEnabled(true);
			maxTrainigSamplesTextBox.setEditable(true);
			removeAllDataButton.setEnabled(model.containsData());
			refreshButtonState();
		}
		else{
			updateInterval.setEditable(false);
			kNearest.setEditable(false);
			createNewClassButton.setEnabled(false);
			removeClassButton.setEnabled(false);
			removeDataButton.setEnabled(false);
			removeAllDataButton.setEnabled(false);
			maxTrainigSamplesTextBox.setEditable(false);
			classLabels.setEnabled(false);
			trainDataButton.setEnabled(modelTrainer!=null);
		}
	}
	
	private void refreshButtonState(){
		removeClassButton.setEnabled(classLabels.getSelectedIndex()>-1);
		trainDataButton.setEnabled(classLabels.getSelectedIndex()>-1);
		removeDataButton.setEnabled(classLabels.getSelectedIndex()>-1);
	}

	private void initComponents() {
		setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
		add(getModelFilePanel());
		add(getModelConfigPanel());
		add(Box.createVerticalGlue());
	}

	private JPanel getModelFilePanel(){
		JPanel panel = new NamedBoxLayoutPanel("Model File",BoxLayout.X_AXIS);
		
		fileName.setMaximumSize(new Dimension(Integer.MAX_VALUE,25));
		fileName.setEditable(false);
		panel.add(fileName);
		panel.add(Box.createHorizontalGlue());
		
		createNewModelButton.addActionListener(createNewModel);
		panel.add(createNewModelButton);
		
		loadModelButton.addActionListener(openModel);
		panel.add(loadModelButton);
		
		saveModelButton.addActionListener(saveModel);
		panel.add(saveModelButton);
		
		saveModelAsButton.addActionListener(saveModelAs);
		panel.add(saveModelAsButton);
		return panel;
	}
	
	private JPanel getModelConfigPanel(){
		JPanel panel = new NamedBoxLayoutPanel("Model Configuration",BoxLayout.Y_AXIS);
		
		panel.add(updateInterval);
		panel.add(kNearest);
		panel.add(maxTrainigSamplesTextBox);
		
		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.Y_AXIS));
		
		Dimension buttonDimension = new Dimension(130,25);
		
		createNewClassButton.setMinimumSize(buttonDimension);
		createNewClassButton.setMaximumSize(buttonDimension);
		createNewClassButton.setPreferredSize(buttonDimension);
		createNewClassButton.addActionListener(addClassLabel);
		buttonPane.add(createNewClassButton);
		
		removeClassButton.setMinimumSize(buttonDimension);
		removeClassButton.setMaximumSize(buttonDimension);
		removeClassButton.setPreferredSize(buttonDimension);
		removeClassButton.addActionListener(removeClassLabel);
		buttonPane.add(removeClassButton);
		
		removeDataButton.setMinimumSize(buttonDimension);
		removeDataButton.setMaximumSize(buttonDimension);
		removeDataButton.setPreferredSize(buttonDimension);
		removeDataButton.addActionListener(removeData);
		buttonPane.add(removeDataButton);
		
		removeAllDataButton.setMinimumSize(buttonDimension);
		removeAllDataButton.setMaximumSize(buttonDimension);
		removeAllDataButton.setPreferredSize(buttonDimension);
		removeAllDataButton.addActionListener(removeAllData);
		buttonPane.add(removeAllDataButton);
		
		trainDataButton.setMinimumSize(buttonDimension);
		trainDataButton.setMaximumSize(buttonDimension);
		trainDataButton.setPreferredSize(buttonDimension);
		trainDataButton.addActionListener(train);
		buttonPane.add(trainDataButton);
		
	
		JPanel listPane = new JPanel();
		listPane.setLayout(new BoxLayout(listPane, BoxLayout.X_AXIS));	
		classLabels.setModel(classLabelModel);
		classLabels.addListSelectionListener(classListChangeListener );
		JScrollPane scrollPane = new JScrollPane(classLabels);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		listPane.add(scrollPane);
		//listPane.add(Box.createHorizontalGlue());
		listPane.add(buttonPane);
		
		panel.add(listPane);
		return panel;
	}

}
